package com.gofaraway.common.util;

import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;

import static java.math.RoundingMode.HALF_UP;

/**
 * 数字相关的便捷工具类
 *
 * @author ChenPeng
 * @Date 2024/8/7 13:29
 */
public class DecimalUtil {

    /**
     * int基本类型的常量：保留小数的位数
     */
    private static final int INT_ZERO = 0;

    /**
     * 2
     */
    private static final int INT_TWO = 2;


    /** ************************************************************************************************************************* */
    /** ************************************************************************************************************************* */
    /** ************************************************************************************************************************* */
    /**
     * BigDecimal类型的常量：100
     * BigDecimal类型的常量：0.01
     */
    private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
    private static final BigDecimal ONE_PERCENT = new BigDecimal("0.01");

    private DecimalUtil() {
        throw new UnsupportedOperationException("静态工具类不允许被实例化");
    }

    /**
     * 绝对值
     *
     * @param number
     * @return
     */
    public static BigDecimal abs(BigDecimal number) {
        return Objects.nonNull(number) ? number.abs() : null;
    }

    /**
     * 将多个BigDecimal列表 按索引相加
     *
     * @param allLists 所有列表
     * @return {@link List}<{@link BigDecimal}> 按索引相加求和后的结果列表
     * @throws IllegalArgumentException 当类表长度不一致是跑出参数错误异常
     */
    public static List<BigDecimal> addBigDecimalLists(List<List<BigDecimal>> allLists) {
        int numLists = allLists.size();
        int listSize = allLists.get(0).size();

        for (int i = 1; i < numLists; i++) {
            if (allLists.get(i).size() != listSize) {
                throw new IllegalArgumentException("相加列表长度不一致");
            }
        }

        List<BigDecimal> result = new ArrayList<>();
        for (int i = 0; i < listSize; i++) {
            BigDecimal sum = BigDecimal.ZERO;
            for (List<BigDecimal> list : allLists) {
                sum = sum.add(list.get(i));
            }
            result.add(sum);
        }
        return result;
    }

    /**
     * 加法运算
     *
     * @param addends 加数
     * @return sum
     */
    public static BigDecimal add(BigDecimal... addends) {
        return Arrays.stream(addends).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
    }

    /**
     * 减法运算
     *
     * @param minute 被减数
     * @param minus  减数
     * @return 差
     */
    public static BigDecimal subtract(BigDecimal minute, BigDecimal minus) {
        return minute.subtract(Optional.ofNullable(minus).orElse(BigDecimal.ZERO));
    }


    /**
     * 减法，v1-v2
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 结果
     */
    public static BigDecimal sub(BigDecimal v1, BigDecimal v2) {
        if (Objects.isNull(v1) || Objects.isNull(v2)) {
            return null;
        }
        return NumberUtil.sub(v1, v2);
    }

    /**
     * 乘法运算
     *
     * @param factors 因子
     * @return 积
     */
    public static BigDecimal multiply(BigDecimal... factors) {
        return Arrays.stream(factors).filter(Objects::nonNull).reduce(BigDecimal::multiply).orElse(BigDecimal.ZERO);
    }

    /**
     * 除法运算， 默认选择四舍五入，默认保留【两位】小数
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 商
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
        return divide(dividend, divisor, INT_TWO, HALF_UP);
    }

    /**
     * 除法运算，默认选择四舍五入
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    需要保留的小数位数
     * @return 商
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor, int scale) {
        return divide(dividend, divisor, scale, HALF_UP);
    }

    /**
     * 除法运算
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    小数位数
     * @param mode     舍入规则
     * @return 商
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor, int scale, RoundingMode mode) {
        // 除数不能为"0"
        if (Optional.of(eq0(divisor)).orElse(Boolean.TRUE)) {
            throw unsupportedException("除数不能为0");
        }
        return dividend.divide(divisor, scale, mode);
    }


    /**
     * 金额转换：元 --> 分
     *
     * @param yuan
     * @return
     */
    public static BigDecimal parseRMBYuan2Fen(BigDecimal yuan) {
        return multiply(yuan, ONE_HUNDRED);
    }

    /**
     * 金额转换：分 --> 元
     *
     * @param fen
     * @return
     */
    public static BigDecimal parseRMBFen2Yuan(BigDecimal fen) {
        return multiply(fen, ONE_PERCENT);
    }

    /**
     * decimalOne > decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean gt(BigDecimal decimalOne, BigDecimal decimalTwo) {
        return decimalOne.compareTo(decimalTwo) > INT_ZERO;
    }

    /**
     * decimalOne >= decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean gtEq(BigDecimal decimalOne, BigDecimal decimalTwo) {
        return decimalOne.compareTo(decimalTwo) >= INT_ZERO;
    }

    /**
     * decimalOne < decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean lt(BigDecimal decimalOne, BigDecimal decimalTwo) {
        return decimalOne.compareTo(decimalTwo) < INT_ZERO;
    }

    /**
     * decimalOne 小于等于 decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean ltEq(BigDecimal decimalOne, BigDecimal decimalTwo) {
        return decimalOne.compareTo(decimalTwo) <= INT_ZERO;
    }

    /**
     * decimalOne == decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean eq(BigDecimal decimalOne, BigDecimal decimalTwo) {
        if (Objects.isNull(decimalOne) && Objects.isNull(decimalTwo)) {
            return Boolean.TRUE;
        }
        if (Objects.isNull(decimalOne) || Objects.isNull(decimalTwo)) {
            return Boolean.FALSE;
        }
        return decimalOne.compareTo(decimalTwo) == INT_ZERO;
    }

    /**
     * decimalOne != decimalTwo
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @param decimalTwo 参与比较的参数
     * @return 返回比较结果
     */
    public static boolean notEq(BigDecimal decimalOne, BigDecimal decimalTwo) {
        return !eq(decimalOne, decimalTwo);
    }

    /**
     * decimalOne < 0
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean lt0(BigDecimal decimalOne) {
        return lt(decimalOne, BigDecimal.ZERO);
    }

    /**
     * decimalOne <= 0
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean ltEq0(BigDecimal decimalOne) {
        return lt(decimalOne, BigDecimal.ZERO) || eq0(decimalOne);
    }

    /**
     * decimalOne > 0
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean gt0(BigDecimal decimalOne) {
        return gt(decimalOne, BigDecimal.ZERO);
    }

    /**
     * decimalOne >= 0
     *
     * @param decimalOne 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean gtEq0(BigDecimal decimalOne) {
        return gt(decimalOne, BigDecimal.ZERO) || eq0(decimalOne);
    }

    /**
     * decimalOne == 0
     *
     * @param compare 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean eq0(BigDecimal compare) {
        return eq(BigDecimal.ZERO, compare);
    }

    /**
     * param != 0
     *
     * @param param 参与比较的参数，一定不能为空，否则会报空指针异常
     * @return 返回比较结果
     */
    public static boolean notEq0(BigDecimal param) {
        return !eq(BigDecimal.ZERO, param);
    }

    /**
     * 取最大值，参数必须非空，否则会报空指针
     *
     * @param decimals
     * @return
     */
    public static BigDecimal max(BigDecimal... decimals) {
        return Arrays.stream(decimals).filter(Objects::nonNull).max(Comparator.comparing(Function.identity())).orElse(null);
    }

    /**
     * 取最大值
     *
     * @param decimals
     * @return
     */
    public static BigDecimal max(List<BigDecimal> decimals) {
        return decimals.stream().filter(Objects::nonNull).max(Comparator.comparing(Function.identity())).orElse(null);
    }

    /**
     * 取最小值
     *
     * @param decimals
     * @return
     */
    public static BigDecimal min(BigDecimal... decimals) {
        return Arrays.stream(decimals).filter(Objects::nonNull).min(Comparator.comparing(Function.identity())).orElse(null);
    }

    /**
     * 取最小值
     *
     * @param decimals
     * @return
     */
    public static BigDecimal min(List<BigDecimal> decimals) {
        return decimals.stream().filter(Objects::nonNull).min(Comparator.comparing(Function.identity())).orElse(null);
    }


    /**
     * 构建异常
     *
     * @param message
     * @return
     */
    public static UnsupportedOperationException unsupportedException(String message) {
        return new UnsupportedOperationException(message);
    }

    /**
     * @param number 数字
     * @param digit  保留几位小数
     *               四舍五入。
     * @return
     */
    public static String keepDecimal(Object number, int digit) {
        if (digit < 0) {
            digit = 0;
        }
        return new BigDecimal(String.valueOf(number)).setScale(digit, HALF_UP).toString();
    }

    /**
     * 非数字判断
     *
     * @param target
     * @return
     */
    public static boolean isNotDecimal(String target) {
        return !isDecimal(target);
    }

    /**
     * 判断是否为数字
     *
     * @param target
     * @return
     */
    public static boolean isDecimal(String target) {
        if (StringUtils.isBlank(target)) {
            return false;
        }
        try {
            new BigDecimal(target);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public static BigDecimal valueOf(Object o, BigDecimal defaultValue) {
        if (o == null) {
            return defaultValue;
        }
        String s = String.valueOf(o);
        if (isNotDecimal(s)) {
            return defaultValue;
        }
        return new BigDecimal(s);
    }

    public static BigDecimal valueOf(Object o) {

        return valueOf(o, BigDecimal.ZERO);
    }

    public static BigDecimal avgValue(List<BigDecimal> decimalList, int scale) {
        if (CollectionUtils.isEmpty(decimalList)) {
            return BigDecimal.ZERO;
        }
        BigDecimal reduce = decimalList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        return reduce.divide(BigDecimal.valueOf(decimalList.size()), scale, HALF_UP);
    }

    public static Integer parseInt(String value) {
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    /**
     * 计算环比， （v2-v1）/v1
     *
     * @param v1
     * @param v2
     * @return
     */
    public static BigDecimal calRelativeRatio(BigDecimal v1, BigDecimal v2) {
        if (Objects.isNull(v1) || Objects.isNull(v2)) {
            return null;
        }
        return NumberUtil.div(NumberUtil.sub(v2, v1), v1);
    }


    /**
     * 保留百分比
     */
    public static String toPercent(BigDecimal v, int scale) {
        if (Objects.isNull(v)) {
            return null;
        }
        return NumberUtil.formatPercent(v.doubleValue(), scale);
    }
}
